#!/home/wcaarls/compile/bin/python
import sys, os, re, arch, jobfarm, time, signal, atexit, copy, math, shutil, thread

def cache(program, system):
    lock.acquire()
    try:
      r = mem[(program, system)]
      #print "% " + str(system) + " is cached"
    except Exception, e:
      lock.release()
      raise e
    lock.release()
    return r

def simulate(program, system, address=None, elaborate=0, postscript=None, lxt=None, noise=0.0):
    #print "simulating " + repr(system)
    if not elaborate and not postscript and not lxt:
      try:
        return cache(program, system)
      except KeyError:
        pass
    
    machines = os.tempnam(dir, "mach-")
    trace = os.tempnam(dir, "trac-")
    report = os.tempnam(dir, "rprt-")

    if isinstance(system, str):
        shutil.copyfile(system, machines)
    else:    
        f = open(machines, "w")
        print >>f, system.GenerateXML(dir+"/" + program + "-machines.xml")
        f.close()

    cmd = dir + "/" + program + " -m " + machines + " -a " + dir + "/" + program + "-availability.xml -o " + dir + "/" + program + "-benchmarks.xml -n master -c -s " + dir + "/" + program + "-trace.xml -t " + trace + " -r " + report + " -N " + str(noise)
    if lxt:
      cmd += " -l " + lxt
    cmd += " >/dev/null"
    
    if __name__ == "__main__":
      print "% " + cmd

    TO = time.time() + 150.0
    peername = 'local'
    
    if address:
      peername = str(address.getpeername())
      jobfarm.start(cmd, True, address)
    else:
      #print "% " + cmd
      os.system(cmd)

    while TO > time.time():
        try:
            f = open(report)
            break
        except IOError, e:
            time.sleep(0.1)
            pass

    if time.time() >= TO:
      print >>sys.stderr, "simulate.simulate: " + cmd + " failed to complete on " + peername
      raise RuntimeError

    eresults = []

    for l in f.readlines():
        m = re.match("  <E time=\"(.*)\"/>", l)
        if m:
          t = float(m.group(1))
        else:
          m = re.match("  <E energy=\"(.*)\"/>", l)
          if m:
            e = float(m.group(1))
          else:
            m = re.match("  <(.*)><E load=\"(.*)\"/>", l)
            if m:
              eresults.append((m.group(1).strip(), float(m.group(2))))
            else:
              m = re.match("  <E predicted-time=\"(.*)\"/>", l)
              if m:
                pt = float(m.group(1))
              else:
                m = re.match("  <E predicted-energy=\"(.*)\"/>", l)
                if m:
                  pe = float(m.group(1))

    global tdiff
    global ediff
    global sims
    
    lock.acquire()
    tdiff = tdiff + abs(t-pt)
    ediff = ediff + abs(e-pe)
    sims = sims + 1
    lock.release()

    f.close()

    if postscript:
        os.system(dir + "/../arch2dot/arch2dot " + machines + " " + report + " " + postscript + ".dot")
        os.system("neato -Tps " + postscript + ".dot >" + postscript)
        os.unlink(postscript+".dot")

    try:
      os.unlink(machines)
    except:
      pass

    try:      
      os.unlink(trace)
    except:
      pass
      
    try:
      os.unlink(report)
    except:
      pass

    lock.acquire()
    mem[(program, system)] = (t, e)
    lock.release()
            
    if elaborate:
      return ((t, eresults), e)
    else:
      return (t, e)

def report():
    print "%% %d simulations, average prediction error %fs / %fJ" % (sims, tdiff/sims, ediff/sims)
    
def cost_old(a):
    r = 0
    a = a.architecture
    p = len(a.components)
    b = a.bandwidth
    l = 0.00010 # lambda for 0.18u

    Q = 5*2*l/10000000 # processor is 5mm^2, trace resolution 2 lambda, 10 MHz
    R = 2.5
    
    x = Q/4
    y = 2*R*x
    z = 2*x
    
    if a.type == "bus":
        #r += p*(26 + 7*b)*l * (22*b+4)*l
        r = b * p * p * x
    elif a.type == "ring":
        #r += p*(26 + 7*b)*l * (22*b+4)*l + p * b * 10 * l * l # registers
        r = b * p * y
    elif a.type == "fc":
        r = b * p * p * p * z
    	#r += p*(26 + 7*b)*l * p*(22*b+4)*l # www.ece.cmu.edu/~bgold/papers/btg_msthesis.pdf

    for c in a.components:
        if c.type == "trimedia":
          r += 30 # 58 in .25
        elif c.type == "rcsimd":
          r += 22 # 22 in .18
        else:
          r += 1  # memory controller

    return r

def cost(a):
    f = 12500000 # 100 MHz
    a = a.architecture
    p = len(a.components)
    b = math.ceil(a.bandwidth / f)
    e = 30 # Average area
    l = 0.00009 # lambda for 0.18u (in mm. Source: http://www.mosis.org/Technical/Designrules/scmos/scmos-main.html)

    r = 0
    
    if a.type == "bus" or a.type == "uniring":
        r += 6 * l * b * p * math.sqrt(e)
    elif a.type == "ring":
        r += 2 * (6 * l * b * p * math.sqrt(e))
    elif a.type == "fc":
        r += 6 * l * b * p * math.sqrt(e) * p
    for c in a.components:
        if c.type == "tm":
          r += 32.32 # (58 in .25); 8.08 in .09
        elif c.type == "rc2":
          r += 25
        elif c.type == "rc4":
          r += 29
        elif c.type == "im132":
          r += 63.79
        elif c.type == "im66":
          r += 63.79
        elif c.type == "xl":
          r += 18 # Xetal2 presentation
        elif c.type == "xl160":
          p = ((18-11.4)/319) # Xetal2 presentation
          r += 11.4 + 159 * p
        elif c.type == "xl640":
          p = ((18-11.4)/319) # Xetal2 presentation
          r += 11.4 + 639 * p
        elif c.type == "ss":
          r += 60 # No idea at all
        elif c.type == "m24kc":
          r += 7.09 # 3.7 in .13
        elif c.type == "camera":
          pass
        else:
          raise ValueError("Unknown processor type")

    return r

mydir = "/home/wcaarls/src/sc/dse"
dir = "/home/wcaarls/src/sc/sctest"
mem = dict()

tdiff = 0
ediff = 0
sims = 0

lock = thread.allocate_lock()

if __name__ == "__main__":
    if len(sys.argv) != 3 and len(sys.argv) != 4 and len(sys.argv) != 5:
        print >>sys.stderr, "Usage:"
        print >>sys.stderr, "  " + sys.argv[0] + " <program> <architecuture> [postscript] [lxt]"
        sys.exit(1)

    if len(sys.argv) > 4:
        lxt = sys.argv[4]
    else:
        lxt = None
        
    if len(sys.argv) > 3:
        postscript = sys.argv[3]
    else:
        postscript = None

    ((t, eresults), e) = simulate(sys.argv[1], sys.argv[2], None, 1, postscript, lxt)

    print "% load = " + str(eresults)
    print "% *** CUT HERE ***"
    print "t = " + str(t) + ";"
    print "e = " + str(e) + ";"
